#include "CEntity.h"
 
std::list<CEntity*> CEntity::EntityList;
const float CEntity::MAX_DISTANCE_PER_MOVE = 0.1; 

CEntity::CEntity()
{
    Surf_Entity = NULL;
    X = 0;
    Y = 0;
    Width = 0;
    Height = 0;
    FacingRight = true;
    Type = ENTITY_TYPE_GENERIC;
    needsCleanup = false;
    Flags = ENTITY_FLAG_GRAVITY;
    SpeedX = 0;
    SpeedY = 0;
    MaxSpeedX = 10;
    MaxSpeedY = 10;
	distanceToMoveX = 0;
	distanceToMoveY = 0;
    CanJump = false;
}

CEntity::~CEntity()
{
}

bool CEntity::OnLoad(char* File, int Width, int Height)
{
    if((Surf_Entity = CSurface::OnLoad(File)) == NULL)
    {
        return false;
    }
    CSurface::Transparent(Surf_Entity, 255, 0, 255);
    this->Width = Width;
    this->Height = Height;
    return true;
}

Acceleration CEntity::UpdateAcceleration()
{
    // Possibly add any acceleration that was accumulated from collisions here.
	float yAccel = 0;
	float xAccel = 0;

	if(Flags & ENTITY_FLAG_GRAVITY)
    {
        yAccel += 0.75f;
    }

	return Acceleration(xAccel,yAccel);
}
 
void CEntity::OnLoop()
{         
	Acceleration accel = UpdateAcceleration();

    SpeedX += accel.getX() * CFPS::FPSControl.GetSpeedFactor();
    SpeedY += accel.getY() * CFPS::FPSControl.GetSpeedFactor();
     
    if(SpeedX > MaxSpeedX)  SpeedX =  MaxSpeedX;
    if(SpeedX < -MaxSpeedX) SpeedX = -MaxSpeedX;
    if(SpeedY > MaxSpeedY)  SpeedY =  MaxSpeedY;
    if(SpeedY < -MaxSpeedY) SpeedY = -MaxSpeedY;

	//Distance(Move) = Speed * time
	distanceToMoveX = SpeedX * CFPS::FPSControl.GetSpeedFactor();
    distanceToMoveY = SpeedY * CFPS::FPSControl.GetSpeedFactor();
     
    OnAnimate();
}
 
void CEntity::OnRender(SDL_Surface* Surf_Display)
{
    if(Surf_Entity == NULL || Surf_Display == NULL) return;
    CSurface::OnDraw(Surf_Display, Surf_Entity, static_cast<int>(X) - CCamera::CameraControl.GetX(), static_cast<int>(Y) - CCamera::CameraControl.GetY(), animExecutor.getAnimationCol() * Width, (animExecutor.GetCurrentFrame()) * Height, Width, Height);
}
 
void CEntity::OnCleanup()
{
    if(Surf_Entity)
    {
        SDL_FreeSurface(Surf_Entity);
    }
 
    Surf_Entity = NULL;
}

void CEntity::OnAnimate()
{    
    animExecutor.OnAnimate();
}

void CEntity::OnCollision(CEntity* Entity)
{
}

bool CEntity::OnMove()
{
    if((SpeedX == 0 && SpeedY == 0) || (distanceToMoveX == 0 && distanceToMoveY == 0))
    {
        return false;
    }

	float moveDistanceThisMoveX = distanceToMoveX;
	float moveDistanceThisMoveY = distanceToMoveY;

	//If distance we have to move this call is too far, throttle it
	if(abs(moveDistanceThisMoveX) > MAX_DISTANCE_PER_MOVE)
	{
		moveDistanceThisMoveX = ((moveDistanceThisMoveX < 0) ? -1 : 1) * MAX_DISTANCE_PER_MOVE;
	}
	
	if(abs(moveDistanceThisMoveY) > MAX_DISTANCE_PER_MOVE)
	{
		moveDistanceThisMoveY = ((moveDistanceThisMoveY < 0) ? -1 : 1) * MAX_DISTANCE_PER_MOVE;
	}

	//Treat X and Y separately for PosValid, because we could hit a wall and still be falling
	if(PosValid(X + moveDistanceThisMoveX, Y))
	{
		X += moveDistanceThisMoveX;
		if(abs(moveDistanceThisMoveX) > abs(distanceToMoveX))
		{
			distanceToMoveX = 0;
		}
		else
		{
			distanceToMoveX -= moveDistanceThisMoveX;
		}
	}
	else
	{
		distanceToMoveX = 0;
		SpeedX = 0;
	}

	if(PosValid(X, Y + moveDistanceThisMoveY))
	{
		Y += moveDistanceThisMoveY;
		if(abs(moveDistanceThisMoveY) > abs(distanceToMoveY))
		{
			distanceToMoveY = 0;
		}
		else
		{
			distanceToMoveY -= moveDistanceThisMoveY;
		}
	}
	else
	{
		distanceToMoveY = 0;
		SpeedY = 0;
	}

	if(distanceToMoveX != 0 || distanceToMoveY != 0)
	{
		return true;
	}

	return false;
}

bool CEntity::Collides(int oX, int oY, int oW, int oH)
{
    int left1, left2;
    int right1, right2;
    int top1, top2;
    int bottom1, bottom2;
    int tX = (int)X ;
    int tY = (int)Y ;
    left1 = tX;
    left2 = oX;
    right1 = left1 + Width - 1 ;
    right2 = oX + oW - 1;
    top1 = tY;
    top2 = oY;
    bottom1 = top1 + Height - 1;
    bottom2 = oY + oH - 1;
    
    if (bottom1 < top2) return false;
    if (top1 > bottom2) return false;
    if (right1 < left2) return false;
    if (left1 > right2) return false;
    return true;
}

bool CEntity::PosValid(int NewX, int NewY)
{
    int StartX = (NewX) / TILE_SIZE;
    int StartY = (NewY) / TILE_SIZE;
    int EndX = ((NewX) + Width - 1) / TILE_SIZE;
    int EndY = ((NewY) + Height - 1) / TILE_SIZE;
    for(int iY = StartY;iY <= EndY;iY++)
    {
        for(int iX = StartX;iX <= EndX;iX++)
        {
            CTile* Tile = CArea::AreaControl.GetTile(iX * TILE_SIZE, iY * TILE_SIZE);
            if(PosValidTile(Tile) == false)
            {
                return false;
            }
        }
    }

    return true;
}

bool CEntity::PosValidTile(CTile* Tile)
{
    if(Tile == NULL || Tile->TypeID == TILE_TYPE_BLOCK)
    {
        return false;
    }
    return true;
}

bool CEntity::PosValidEntity(CEntity* Entity, int NewX, int NewY)
{
    if(this != Entity && Entity != NULL && Entity->needsCleanup == false &&
        !(Entity->Flags & ENTITY_FLAG_MAPONLY) &&
        Entity->Collides(NewX , NewY , Width  - 1, Height  - 1) == true)
    {
        CEntityCol EntityCol;
        EntityCol.EntityA = this;
        EntityCol.EntityB = Entity;
        CEntityCol::EntityColList.push_back(EntityCol);
        return false;
    }
    return true;
}

bool CEntity::Jump()
{
    if(CanJump == false)
    {
        return false;
    }
    SpeedY = -MaxSpeedY;
    return true;
}